
// 
// 101126 - AH - Created.
// 101127a- AH - Finished implementation.
// 101127b- AH - Appropriate to use binary search for indexOf (even if probably
//               does not matter) so does no.
// 101127c- AH - Made iterable over tags.
// 101127d- AH - clear().
// 110410 - AH - (Fantoft Club) Tags(Collection<String>).
// 110531 - AH - contains(String) is now no longer case sensitive.
// 110603 - AH - (Garage) A tag is no longer a String but SoekrateName objects.
// 110615 - AH - getIndexOf(SoekratName).
// 110617a- AH - Removed commented away code.
// 110617b- AH - create().
// 110617c- AH - remove(SoekratName).
//

package org.soekrat.engine.mine;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.soekrat.engine.SoekratName;

/**
 * <p>
 *   Resource's tags.
 * </p>
 * @author Arne Halvorsen (AH)
 * @author Laetitia Le Chatton (LLC) 
 */
public final class Tags implements Iterable<SoekratName> 
{
  private final List<SoekratName> m_tags=new ArrayList<SoekratName>();
  
  /**
   * <p>
   *   Constructor.
   * </p>
   */
  public Tags(){}
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param r Representation as produced by
   *          {@link #toString()} : a comma separated list.
   */
  public Tags(String r)
  {
    if (r==null)
    {
      throw new NullPointerException("r");
    }
    
    StringTokenizer st=new StringTokenizer(r, ",");
    while (st.hasMoreTokens())
    {
      String tag=st.nextToken().trim();
      if (tag.length()>0) m_tags.add(new SoekratName(tag));
    }
  }
  
  /**
   * <p>
   *   Constructor.
   * </p>
   * @param tags Tags.
   */
  public Tags(Collection<String> tags)
  {
    if (tags==null)
    {
      throw new IllegalArgumentException("tags");
    }
    
    for (String tag : tags)
    {
      tag=tag.trim();
      if (tag.length()>0) m_tags.add(new SoekratName(tag));
    }
  }
  
  /**
   * <p>
   *   Adds tag.
   * </p>
   * @param tag Tag to add.
   * @return {@code true} if tag added, {@code false} if tag already added.
   */
  public boolean add(SoekratName tag)
  {
    if (tag==null)
    {
      throw new NullPointerException("tag");  
    }
    
    if (!contains(tag.toString()))
    {
      m_tags.add(tag);
      return true;
    }
    return false;
  }
  
  /**
   * <p>
   *   Adds tag.
   * </p>
   * @param tag Tag to add.
   * @return {@code true} if tag added, {@code false} if tag already added.
   */
  public boolean add(String tag)
  {
    if (tag==null)
    {
      throw new NullPointerException("tag");  
    }
    
    if (!contains(tag))
    {
      m_tags.add(new SoekratName(tag));
      return true;
    }
    return false;
  }
  
  /**
   * <p>
   *   Removes tag.
   * </p>
   * @param tag Tag.
   * @return {@code true} if tag removed, {@code false} if not found.
   */
  public boolean remove(String tag)
  {
    if (tag==null)
    {
      throw new NullPointerException("tag");  
    }
    
    return m_tags.remove(tag);
  }
  
  /**
   * <p>
   *   Removes all tags.
   * </p>
   */
  public void clear(){ m_tags.clear(); }
  
  /**
   * <p>
   *   Tells if tag is in set.
   * </p>
   * @param tag Tag.
   * @return {@code true} if tag is in set, {@code false} if it is not.
   */
  public boolean contains(String tag)
  {
    if (tag==null)
    {
      throw new NullPointerException(tag);
    }
    
    for (SoekratName curr : m_tags)
    {
      if (curr.equals(tag)) return true;
    }
    
    return false;
  }
  
  /**
   * <p>
   *   Creates tag with initial name.
   * </p>
   * @return Created.
   */
  public SoekratName create()
  {
    int n=1;
    while (true)
    {
      String name="New_tag_"+n;
      if (!contains(name))
      {
        SoekratName retVal=new SoekratName(name);
        add(retVal);
        return retVal;
      }
      n++;
    }
  }
  
  /**
   * <p>
   *   Removes tag.
   * </p>
   * @param tag Tag.
   * @return Index of removed or {@code -1} if not found.
   */
  public int remove(SoekratName tag)
  {
    if (tag==null)
    {
      throw new NullPointerException("tag");
    }
    
    int idx=getIndexOf(tag);
    if (idx==-1) return -1;
    m_tags.remove(idx);
    return idx;
  }
  
  /**
   * <p>
   *   Gets number of tags.
   * </p>
   * @return Size.
   */
  public int size(){ return m_tags.size(); }
  
  /**
   * <p>
   *   Tells if set is empty or not.
   * </p>
   * @return {@code true} if is, {@code false} if is not.
   */
  public boolean isEmpty(){ return m_tags.isEmpty(); }
  
  /**
   * <p>
   *   Gets tag.
   * </p>
   * @param idx Tag's index.
   * @return Tag.
   * @throws IndexOutOfBoundsException If {@code idx<0 || idx>=size()}.
   */
  public SoekratName getTag(int idx){ return m_tags.get(idx); }
  
  /**
   * <p>
   *   Gets index of tag.
   * </p>
   * @param tag Tag.
   * @return Index or {@code -1} if not found.
   */
  public int getIndexOf(String tag)
  {
    if (tag==null)
    {
      throw new NullPointerException("tag");
    }
    
    int n=m_tags.size();
    for (int i=0; i<n; i++)
    {
      SoekratName name=m_tags.get(i);
      if (name.equals(tag)) return i;
    }
    return -1;
  }
  
  /**
   * <p>
   *   Gets index of tag.
   * </p>
   * @param name {@link SoekratName} of tag to seek.
   * @return Index or {@code -1} if not found.
   */
  public int getIndexOf(SoekratName name)
  {
    if (name==null)
    {
      throw new NullPointerException("name");
    }
    
    int n=m_tags.size();
    for (int i=0; i<n; i++)
    {
      SoekratName nme=m_tags.get(i);
      if (nme.equals(name)) return i;
    }
    return -1;
  }
  
  // Specified in Iterable<String>.
  public Iterator<SoekratName> iterator(){ return m_tags.iterator(); }

  /**
   * <p>
   *   Gets
   *   {@code String} representation.
   * </p>
   * @return {@code String} representation: A comma separated list.
   */
  public String toString()
  {
    StringBuilder sb=new StringBuilder();
    boolean first=true;
    for (SoekratName tag : m_tags)
    {
      if (!first) sb.append(",");
      first=false;
      sb.append(tag);
    }
    return sb.toString();
  }
  
}
